/*global console, window, $, OpenLayers, google, MAIN*/
$( function () {

    /**
     * Main Script for Map Overlay Annotation tool
     */
    'use strict';
    var errorCount = 0;
    $.extend({
        /**
         * fetchMap: called to fetch geoJSON objects from server backend.  Expects
         * to recieve an array of geojson objects.  Callback is invoked when data
         * is successfully read and passed the array of objects.
         */
        fetchMap: function(postUrl, callback) {
            $.ajax({
                url: postUrl,
                success: function(data) {
                    var geoJSONArray = [];
                    $.each(data, function(unused, geoJSONAsText) {
                        geoJSONArray.push(JSON.parse(geoJSONAsText.geojson));
                    });
                    $("#error").remove();
                    callback(geoJSONArray);
                },
                dataType: "json",
                error: function(jqXHR) {
                    if ($("#error").length === 0) {
                        $("#page").append($("<div id='error'>").addClass("ui-state-error"));
                        errorCount = 0;
                    }
                    errorCount += 1;
                    $("#error").html($("<p>Lost connection to database. Retrying" + Array(errorCount).join(".") + "<\/p>"));
                    $("div#error").dialog({
                        closeOnEscape: false,
                        open: function(event, ui) { $(".ui-dialog-titlebar-close").hide();}
                    });
                }
            });
        }
    });

    $.extend({
        /**
         * initalizeMap: called to create a new openlayers map on a google physical
         * layers map.  Returns an object describing the lat/lng projection and the
         * OpenLayers map
         */
        initalizeMap: function() {
            var googlePhysicalLayer, googleSatelliteLayer;
            //Initialize map, 'map_div' is the id of the div on the main page.
            //it would be nice if this weren't hard coded
            MAIN.variables.map = new OpenLayers.Map("map_div", {
                projection: new OpenLayers.Projection("EPSG:900913")
            });
            var layers = [new OpenLayers.Layer.Google("Google Physical", {
                type: google.maps.MapTypeId.TERRAIN
            }), new OpenLayers.Layer.Google("Google Satellite", {
                type: google.maps.MapTypeId.SATELLITE,
		numZoomLevels: 22
            }), new OpenLayers.Layer.Google("Google Streets", {
		numZoomLevels: 20
            }), new OpenLayers.Layer.OSM()];

            MAIN.variables.map.addLayers(layers);

            //This is important, but don't remember the details.  Has to do with
            //converting lat/lng to projected coordinates.  Is this a lat/lng projection?
            MAIN.variables.lngLatProj = new OpenLayers.Projection("EPSG:4326");
            MAIN.variables.map.addControl(new OpenLayers.Control.LayerSwitcher());

            // Google.v3 uses EPSG:900913 as projection, so we have to
            // transform our coordinates
            MAIN.variables.map.setCenter(new OpenLayers.LonLat(MAIN.variables.viewport.lng,
            MAIN.variables.viewport.lat).transform(MAIN.variables.lngLatProj,
            MAIN.variables.map.getProjectionObject()),
            MAIN.variables.viewport.zoom);

            var defaultStyleMap = new OpenLayers.StyleMap({
                'default': OpenLayers.Util.applyDefaults({
                    fillColor: '#aaa',
                    fillOpacity: 0.4,
                    strokeWidth: 1,
                    strokeColor: '#333',
                    pointRadius: 6
                }),
                'select': OpenLayers.Util.applyDefaults({
                    fillColor: '#ccc',
                    fillOpacity: 0.7,
                    strokeWidth: 3,
                    strokeColor: '#eee',
                    pointRadius: 6
                })
            });

            var existingStyleMap = new OpenLayers.StyleMap({
                'default': OpenLayers.Util.applyDefaults({
                    fillColor: '#0fa',
                    fillOpacity: 0.4,
                    strokeWidth: 1,
                    strokeColor: '#084',
                    pointRadius: 6
                }),
                'select': OpenLayers.Util.applyDefaults({
                    fillColor: '#0f3',
                    fillOpacity: 0.7,
                    strokeWidth: 3,
                    strokeColor: '#0f3',
                    pointRadius: 6
                })
            });

            var futureStyleMap = new OpenLayers.StyleMap({
                'default': OpenLayers.Util.applyDefaults({
                    fillColor: '#f09',
                    fillOpacity: 0.4,
                    strokeWidth: 1,
                    strokeColor: '#a05',
                    pointRadius: 6
                }),
                'select': OpenLayers.Util.applyDefaults({
                    fillColor: '#f03',
                    fillOpacity: 0.7,
                    strokeWidth: 3,
                    strokeColor: '#f03',
                    pointRadius: 6
                })
            });

            MAIN.variables.layers.defaultLayer = new OpenLayers.Layer.Vector("Unclassified", {
                styleMap: defaultStyleMap
            });

            MAIN.variables.layers.baseLayer = new OpenLayers.Layer.Vector("Base Layer", {
                styleMap: defaultStyleMap
            });

            MAIN.variables.layers.existingLayer = new OpenLayers.Layer.Vector("Existing", {
                styleMap: existingStyleMap
            });

            MAIN.variables.layers.futureLayer = new OpenLayers.Layer.Vector("Future", {
                styleMap: futureStyleMap
            });

            var layerArray = [];
            $.each(MAIN.variables.layers, function(unused, layer) {
                MAIN.variables.map.addLayer(layer);
                layerArray.push(layer);
            });
            //http://dev.openlayers.org/docs/files/OpenLayers/Control/SelectFeature-js.html
            var hoverControls = new OpenLayers.Control.SelectFeature(layerArray, {
                hover: true
            });
            var div = $('<div>').css({
                'display': 'none',
                'background-color': '#fff',
                'border-width': 1,
                'border-color': '#555',
                'border-style': 'solid',
                'border-radius': 5,
                '-moz-border-radius': 15,
                'z-index': 1000
            }).hide().appendTo('body');
            var hoverFunction = MAIN.callbacks.hoverFunction(div);

            hoverControls.events.register("featurehighlighted",this, function(e) {
                var text = "<table>";
                $.each(e.feature.attributes, function(key, value) {
                    text = text + "<tr><th style='text-align: right'>" + key + "<\/th><td>" + value + "<\/td><\/tr>";
                });
                text = text + '<\/table>';
                div.html(text);
                hoverFunction = MAIN.callbacks.hoverFunction(div);
                //bind mousemove to the function that changes the position of the div
                $('body').mousemove(hoverFunction);
            });
            hoverControls.events.register("featureunhighlighted",this, function(e) {
                $('body').unbind('mousemove', hoverFunction);
                div.hide();
            });
            //This is to handle cases where poygons abut the edge of the map and
            //the user mouses out.  Unfortunately mousing back in doesn't re-enable
            //the hover control.
            $("div#info").hover( function(e) {
                hoverControls.unselectAll();
            });
            //after constructing hover controls, add them to the map and activate
            MAIN.variables.map.addControl(hoverControls);
            hoverControls.activate();

        }
    });

    /**
     * This funciton is called on pageload.  Initializes the map and all the controls.
     * It's the main applciation.
     */
    $( function main() {

        //This function and following code makes sure the map takes
        //up most of the page to look good
        var userOverlaySources, rasterLayerSources;

        //when the window is resized, call this function,
        //also get it queued up to call as soon as possible
        $(window).resize(MAIN.callbacks.resizeFunction).resize();

        //Turn info screen into collapsable element
        $("#info").accordion({
            collapsible: true,
            change: MAIN.callbacks.resizeFunction,
            autoHeight: false
        });

        // This fetches a JSON object to point to the predefined user overlays
        $.ajax({
            type: 'GET',
            url: 'data/user_overlays.json',
            dataType: "json",
            async: false, // This is to get the callback to the userlayer set properly
            success: function(data) {
                userOverlaySources = data.layers;
                rasterLayerSources = data.rasters;
                MAIN.variables.viewport = {
                    zoom: data.zoom,
                    lat: data.lat,
                    lng: data.lng
                };

                //Load the user defined base layers and insert them in userLayer
                $.each(userOverlaySources || [], function(unused, source) {
                    $.ajax({
                        type: 'GET',
                        url: source,
                        dataType: "text",
                        async: false, // This is to get the callback to the userlayer set properly
                        success: function(data) {
                            var featureCollection, jsonParser;
                            jsonParser = new OpenLayers.Format.GeoJSON();
                            featureCollection = jsonParser.read(data);

                            MAIN.variables.layers.baseLayer.addFeatures(featureCollection);

                        },
                        error: function(jqXHR, textStatus, errorThrown) {
                            console.error("Couldn't fetch " + source);
                            console.log(jqXHR);
                            console.log(textStatus);
                            console.log(errorThrown);
                        }
                    });
                });
                //Load iraster layers and insert them in an image layer
                $.each(rasterLayerSources || [], function(unused, source) {
                    var imageLayer = new OpenLayers.Layer.Image(source.label, source.src,
                    new OpenLayers.Bounds(source.bounds[0],source.bounds[1],source.bounds[2],source.bounds[3]),
                    new OpenLayers.Size(source.size[0], source.size[1]), {
                        isBaseLayer: false,
                        alwaysInRange: true,
                        projection: new OpenLayers.Projection("EPSG:900913")
                    });
                    MAIN.variables.map.addLayers([imageLayer]);
                    MAIN.variables.map.zoomToExtent(new OpenLayers.Bounds(source.bounds[0],source.bounds[1],source.bounds[2],source.bounds[3]));
                });
            },
            error: function(jqXHR, textStatus, errorThrown) {
                console.error("Couldn't fetch data/user_overlays.json");
                console.log(jqXHR);
                console.log(textStatus);
                console.log(errorThrown);
            }
        });

        $.initalizeMap();

        var loadFeaturesFromDatabase = ( function () {
            var oldGeometryObject = {
                Point: [],
                Polygon: [],
                LineString: []
            };
            var geoJSONParser = new OpenLayers.Format.GeoJSON();
            return function() {
                console.log("loading");
                $.fetchMap(MAIN.variables.cgi_path + '/get_data.php', function(geoJSONArray) {
                    //visit every geoJSON object.  these represent UUIDs
                    var geometryObject = {
                        Point: [],
                        Polygon: [],
                        LineString: []
                    };

                    //loop over each entry
                    var i;
                    for (i = 0; i < geoJSONArray.length; i += 1) {
                        var geoJSON = geoJSONArray[i];
                        //each geometry type
                        $.each(['LineString', 'Point', 'Polygon'], function(unused, featureType) {
                            //each entry of that geometry type
                            $.each(geoJSON[featureType].features, function(unused, polyData) {
                                var geometry = geoJSONParser.parseGeometry(polyData.geometry);
                                //store the properties from the geoJSON file inside the
                                //OpenLayers geometry object, this is used later on hover events
                                //to print out a div with those properties
                                geometry.properties = polyData.properties;
                                geometryObject[featureType].push(geometry);
                            });
                        });
                    }

                    $.each(geometryObject, function(type, geometry) {
                        var i;
                        for (i = oldGeometryObject[type].length; i < geometryObject[type].length; i += 1) {
                            var geometry = geometryObject[type][i];

                            var feature = new OpenLayers.Feature.Vector(geometry, null, null);
                            feature.attributes = geometry.properties;

                            //Transform coordinates from lat/lng into the map's projection
                            geometry.transform(MAIN.variables.lngLatProj, MAIN.variables.map.getProjectionObject());
                            if (geometry.properties['Feature-Type']) {
                                if (geometry.properties['Feature-Type'] === 'Future') {
                                    MAIN.variables.layers.futureLayer.addFeatures([feature]);
                                } else {
                                    MAIN.variables.layers.existingLayer.addFeatures([feature]);
                                }
                            } else {
                                MAIN.variables.layers.defaultLayer.addFeatures([feature]);
                            }
                        }
                    });
                    oldGeometryObject = geometryObject;
                });
                $.timer(loadFeaturesFromDatabase).once(1000);
            }
        }());
        loadFeaturesFromDatabase();
    });
});